/*  SSEventObject.m
 *  MESLib
 * 
 * Copyright 2003-2009 Starline Software Ltd
 * 
 * This file is part of MESLib.
 * 
 * MESLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MESLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with MESLib. If not, see <http://www.gnu.org/licenses/>.
 */
//  Created by Tobias Haughton on Wed Jan 22 2003.
//
//the SSEventObject class represents a single event. It holds and allows for the manipulation
//of event data. It also holds an SSEventView object which is used to display the event to the user

#import "SSEventObject.h"
#import "SSEventLinkObject.h"
#import "SSCatalystDocument.h"

@implementation SSEventObject

- (id)initWithUnique_id:(NSString *)unique
                  actor:(NSString *)act
                 action:(NSString *)actio
                 object:(NSString *)obj
               location:(NSString *)loc
              beginTime:(SSChartDate *)begin
                endTime:(SSChartDate *)end
                 source:(NSArray *)sou
                remark:(NSString *)rem
                address:(NSString *)add
                  label:(NSNumber *)lab
           passedNSTest:(BOOL)passed
              xPosition:(float)xPos
                  links:(NSMutableArray *)links
{
   if (self = [super init]) {
      isLoading = YES;
      [self setUnique_id:unique];
      [self setActor:act];
      [self setAction:actio];
      [self setObject:obj];
      [self setLocation:loc];
      [self setBeginTime:begin];
      [self setEndTime:end];
      [self setSource:sou];
      [self setRemark:rem];
      [self setAddress:add];
      [self setLabel:lab];
      [self setLinkArray:links];
      [self setPassedNSTest:passed];

      linkFromArray = [[NSMutableArray alloc] init];
      needsDiamondArray = [[NSMutableArray alloc] init];

      //initilize the event view
      NSRect frame;
      frame.origin = NSMakePoint(xPos, 0);
      frame.size = SSEventViewDefaultSize;
      eventView = [[SSEventView alloc] initWithFrame:frame owner:self isGhostView:NO];

      isVisible = YES;

      isLoading = NO;
   }
   return self;
}

- (id)initWithActor:(NSString *)act
             action:(NSString *)actio
             object:(NSString *)obj
           location:(NSString *)loc
          beginTime:(SSChartDate *)begin
            endTime:(SSChartDate *)end
             source:(NSArray *)sou
            remark:(NSString *)rem
            address:(NSString *)add
              label:(NSNumber *)lab;
{
   if (self = [super init]) {
      isLoading = YES;
      SSChartDate *now = [SSChartDate calendarDate];
      NSString *unique = [NSString stringWithFormat:@"%@%@", act, [now descriptionWithCalendarFormat:@"%y%m%d%H%M%F"]];
      [self setUnique_id:unique];
      [self setActor:act];
      [self setAction:actio];
      [self setObject:obj];
      [self setLocation:loc];
      [self setBeginTime:begin];
      [self setEndTime:end];
      [self setSource:sou];
      [self setRemark:rem];
      [self setAddress:add];
      [self setLabel:lab];
      [self setLinkArray:[NSMutableArray array]];
      linkFromArray = [[NSMutableArray alloc] init];
      needsDiamondArray = [[NSMutableArray alloc] init];

      //initilize the event view
      NSRect frame;
      frame.origin = NSMakePoint(0, 0);
      frame.size = SSEventViewDefaultSize;
      eventView = [[SSEventView alloc] initWithFrame:frame owner:self isGhostView:NO];

      isVisible = YES;
      isLoading = NO;
   }
   return self;
}


- (id)initAsGhostEventWithAddress:(NSString *)add label:(NSNumber *)lab xPosition:(float)xPos
{
   if (self = [super init]) {
      isLoading = YES;
      
      SSChartDate *now = [SSChartDate calendarDate];
      NSString *unique = [now descriptionWithCalendarFormat:@"%y%m%d%H%M%F"];
      [self setUnique_id:unique];

      [self setActor:@"Ghost"];

      [self setAction:[NSString string]];

      [self setObject:[NSString string]];

      [self setLocation:[NSString string]];

      SSChartDate *begin = [SSChartDate calendarDate];
      [self setBeginTime:begin];

      SSChartDate *end = [SSChartDate calendarDate];
      [self setEndTime:end];

      [self setSource:[NSArray array]];

      [self setRemark:[NSString string]];
      
      [self setLinkArray:[NSMutableArray array]];

      [self setAddress:add];

      [self setLabel:label];

      linkFromArray = [[NSMutableArray alloc] init];
      needsDiamondArray = [[NSMutableArray alloc] init];

      //initilize the event view
      NSRect frame;
      frame.origin = NSMakePoint(xPos, 0);
      frame.size = SSEventViewDefaultSize;
      eventView = [[SSEventView alloc] initWithFrame:frame owner:self isGhostView:YES];
      
      isVisible = YES;
      
      isLoading = NO;
   }
   return self;
}

- (id)initWithPboardRepresentation:(NSMutableDictionary *)dict xPos:(float)xPos
{
   if (self = [super init]) {
      isLoading = YES;
      [self setUnique_id:[dict objectForKey:@"unique_id"]];
      [self setActor:[dict objectForKey:@"actor"]];
      [self setAction:[dict objectForKey:@"action"]];
      [self setObject:[dict objectForKey:@"object"]];
      [self setLocation:[dict objectForKey:@"location"]];
      SSChartDate *begin = [[[SSChartDate alloc] initWithString:[dict objectForKey:@"beginTime"] calendarFormat:@"%Y-%m-%d-%H-%M-%S-%F"] autorelease];
      [begin setIsEstimated:[[dict objectForKey:@"beginTimeIsEstimated"] intValue]];
      [self setBeginTime:begin];
      SSChartDate *end = [[[SSChartDate alloc] initWithString:[dict objectForKey:@"endTime"] calendarFormat:@"%Y-%m-%d-%H-%M-%S-%F"] autorelease];
      [end setIsEstimated:[[dict objectForKey:@"endTimeIsEstimated"] intValue]];
      [self setEndTime:end];
      [self setSource:[dict objectForKey:@"source"]];
      [self setRemark:[dict objectForKey:@"remark"]];
      [self setAddress:[dict objectForKey:@"address"]];
      [self setLabel:[dict objectForKey:@"label"]];
      
      linkFromArray = [[NSMutableArray alloc] init];
      needsDiamondArray = [[NSMutableArray alloc] init];
      
      //initilize the event view
      NSRect frame;
      frame.origin = NSMakePoint(xPos, 0);
      frame.size = SSEventViewDefaultSize;
      eventView = [[SSEventView alloc] initWithFrame:frame owner:self isGhostView:NO];
      
      isVisible = YES;
      
      isLoading = NO;
   }
   return self;
}

- (NSString *)unique_id
{
   return unique_id;
}

- (NSString *)actor
{
   return actor;
}

- (NSString *)action
{
   return action;
}

- (NSString *)object
{
   return object;
}

- (NSString *)location
{
   return location;
}

- (SSChartDate *)beginTime
{
   return beginTime;
}

- (NSString *)beginTimeString
{
   return [beginTime descriptionWithCalendarFormat:@"%Y:%m:%d %H:%M:%S:%F"];
}

- (SSChartDate *)endTime
{
   return endTime;
}

- (NSString *)endTimeString
{
   return [endTime descriptionWithCalendarFormat:@"%Y:%m:%d %H:%M:%S:%F"];
}

- (NSArray *)source
{
   return source;
}

- (NSString *)sourceString
{
   NSString *string = [NSString string];
   int i = 0;

   while(i < [source count]) {
      //build a string from our array
      string = [string stringByAppendingString:[source objectAtIndex:i]];
      i++;

      if(i < [source count]) {
         //we are going to add another value so add a ,
         string = [string stringByAppendingString:@", "];
      }
   }
   
   return string;
}

- (NSString *)remark
{
   return remark;
}

- (NSMutableArray *)linkArray
{
   return linkArray;
}

- (SSEventLinkObject *)linkObjectConnectingEvent:(SSEventObject *)event
{
   //return the link object which links us to event
   NSEnumerator *enumerator = [linkArray objectEnumerator];
   id anObject;

   while (anObject = [enumerator nextObject]) {
      if([[anObject linkedTo] isEqualTo:event]) {
         return anObject;
      }
   }

   return nil;
}

- (NSMutableArray *)linkFromArray
{
   return linkFromArray;
}

- (NSString *)address
{
   return address;
}

- (NSString *)fileName
{
   return [[[self address] pathComponents] lastObject]; //return the actual file name rather that the path
}

- (SSEventView *)eventView
{
   return eventView;
}

- (NSNumber *)label
{
   return label;
}

- (NSString *)labelString
{
   //return a label string based on our label
   switch([[self label] intValue]) {
      case SSYellowLabelType:return @"yellow";
         break;
      case SSGreenLabelType:return @"green";
         break;
      case SSLightBlueLabelType:return @"light blue";
         break;
      case SSBlueLabelType:return @"blue";
         break;
      case SSPurpleLabelType:return @"purple";
         break;
      case SSOrangeLabelType:return @"orange";
         break;
      case SSRedLabelType:return @"red";
         break;
      default:
         return @"white";
         break;
   }
}

- (BOOL)dataChanged
{
   return dataChanged;   
}

- (NSMutableArray *)needsDiamondArray
{
   return needsDiamondArray;
}

- (BOOL)passedNSTest
{
   return passedNSTest;
}

- (BOOL)isVisible
{
   return isVisible;
}

- (void)setUnique_id:(NSString *)string
{
   [unique_id release];
   [string retain];
   unique_id = string;
}

- (void)setActor:(NSString *)string
{
   if(!isLoading) {
      //post a notification so that this objects view will be moved to the correct row
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSPrepareForReorderingFromActor"
                                                          object:[[[self actor] copy] autorelease]];
   }
   
   [actor release];
   [string retain];
   actor = string;

   if(!isLoading) {
      //post a notification so that this objects info will be updated in the aalosDictionary
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSActorChanged"
                                                          object:self];
      //post a notification so that this objects view will be moved to the correct row
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSActorChangedForView"
                                                          object:[self eventView]];
      [eventView updateActor];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setAction:(NSString *)string
{
   [action release];
   [string retain];
   action = string;

   if(!isLoading) {
      //post a notification so that this objects info will be updated in the aalosDictionary
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSActionChanged"
                                                          object:self];
      [eventView updateAction];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setObject:(NSString *)string
{
   [object release];
   [string retain];
   object = string;
   
   if(!isLoading) {
      //post a notification so that this objects info will be updated in the aalosDictionary
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSObjectChanged"
                                                          object:self];
      [eventView updateObject];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setLocation:(NSString *)string
{
   [location release];
   [string retain];
   location = string;
   
   if(!isLoading) {
      //post a notification so that this objects info will be updated in the aalosDictionary
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSLocationChanged"
                                                          object:self];
      [eventView updateLocation];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setSource:(NSArray *)array
{
   if([array isKindOfClass:[NSArray class]]) {
      [source release];
      [array retain];
      source = array;

      if(!isLoading) {
         //post a notification so that this objects info will be updated in the aalosDictionary
         [[NSNotificationCenter defaultCenter] postNotificationName:@"SSSourceChanged"
                                                             object:self];
         [eventView updateSource];

         dataChanged = YES;

         [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
      }
   }   
}

- (void)setRemark:(NSString *)string
{
   [remark release];
   [string retain];
   remark = string;
   
   if(!isLoading) {
      //post a notification so that this objects info will be updated in the aalosDictionary
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSRemarkChanged"
                                                          object:self];
      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setBeginTime:(SSChartDate *)cd
{
   if(!isLoading) {
      //check that the begin time is earlier than the end and warn if not
      if([cd compare:[self endTime]] == NSOrderedDescending) {
         //warn about date problem
         int choice = NSRunAlertPanel(NSLocalizedString(@"Alert", nil), NSLocalizedString(@"EndDateIsBeforeBegin", nil), NSLocalizedString(@"Revert", nil), NSLocalizedString(@"Continue", nil), nil);
         if(choice == NSAlertDefaultReturn) {
            //user wants to cancel change
            [eventView updateBeginTime];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"SSUpdateAuxiliaryPanels"
                                                                object:nil];
            return;
         }  
      }
      
      //register to undo this change
      NSUndoManager* aManager = [[[NSDocumentController sharedDocumentController] currentDocument] undoManager];
      [aManager registerUndoWithTarget:self selector:@selector(setBeginTime:) object:beginTime];
   }
   
   [cd retain];
   [beginTime release];
   beginTime = cd;
   
   if(!isLoading) {
      //post a notification so that the tracking cell will update
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSEventObjectBeginTimeChanged"
                                                          object:self];
      [eventView updateBeginTime];

      //get the document to see if we share a begin time with other events with the same name and display an alert if so
      [[[NSDocumentController sharedDocumentController] currentDocument] validateEventBeginTime:self];
      
      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setBeginTimeNoWarning:(SSChartDate *)cd
{
   if(!isLoading) {
      //register to undo this change
      NSUndoManager* aManager = [[[NSDocumentController sharedDocumentController] currentDocument] undoManager];
      [aManager registerUndoWithTarget:self selector:@selector(setBeginTime:) object:beginTime];
   }

   [cd retain];
   [beginTime release];
   beginTime = cd;

   if(!isLoading) {
      //post a notification so that the tracking cell will update
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SSEventObjectBeginTimeChanged"
                                                          object:self];
      [eventView updateBeginTime];

      //get the document to see if we share a begin time with other events with the same name and display an alert if so
      [[[NSDocumentController sharedDocumentController] currentDocument] validateEventBeginTime:self];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setBeginTimeStatus:(BOOL)b
{
   [beginTime setIsEstimated:b];

   if(!isLoading) {
      [eventView updateBeginTime];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setEndTime:(SSChartDate *)cd
{
   if(!isLoading) {
      //check that the end time is later than the begin and warn if not
      if([cd compare:[self beginTime]] == NSOrderedAscending) {
         //warn about date problem
         int choice = NSRunAlertPanel(NSLocalizedString(@"Alert", nil), NSLocalizedString(@"EndDateIsBeforeBegin", nil), NSLocalizedString(@"Revert", nil), NSLocalizedString(@"Continue", nil), nil);
         if(choice == NSAlertDefaultReturn) {
            //user wants to cancel change
            [eventView updateEndTime];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"SSUpdateAuxiliaryPanels"
                                                                object:nil];
            
            return;
         }   
      }
   }
   
   [cd retain];
   [endTime release];
   endTime = cd;

   if(!isLoading) {
      [eventView updateEndTime];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setEndTimeNoWarning:(SSChartDate *)cd
{
   [cd retain];
   [endTime release];
   endTime = cd;

   if(!isLoading) {
      [eventView updateEndTime];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setEndTimeStatus:(BOOL)b
{
   [endTime setIsEstimated:b];

   if(!isLoading) {
      [eventView updateEndTime];

      dataChanged = YES;

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setLinkArray:(NSMutableArray *)array
{
   [linkArray release];
   [array retain];
   linkArray = array;
}

- (void)setLinkFromArray:(NSMutableArray *)array
{
   [linkFromArray release];
   [array retain];
   linkFromArray = array;
}

- (void)setEventView:(SSEventView *)view
{
   [eventView release];
   [view retain];
   eventView = view;
}

- (void)setAddress:(NSString *)string
{
   [address release];
   [string retain];
   address = string;
}

- (void)setLabel:(NSNumber *)num
{
   [label release];
   [num retain];
   label = num;

   if(!isLoading) {
      //update the event view
      [eventView setNeedsDisplay:YES];

      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setPassedNSTest:(BOOL)passed
{
   passedNSTest = passed;

   if(!isLoading) {
      dataChanged = YES;
      [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
   }
}

- (void)setXPosition:(float)xPos
{
   [eventView setFrameOrigin:NSMakePoint(xPos, NSMinY([eventView frame]))];

   if([eventView displayMode] == SSOverlapDisplayMode) {
      //update our dates
      [eventView updateDatesAfterExternalPositionChange];
   }
}

- (void)setIsVisible:(BOOL)b
{
   isVisible = b;   
}

- (void)setNeedsDiamondArray:(NSMutableArray *)array
{
   [needsDiamondArray release];
   [array retain];
   needsDiamondArray = array;
}

- (void)addNeedsDiamond:(SSNeedsDiamond *)diamond
{
   [diamond setOwner:self];
   [needsDiamondArray addObject:diamond];
}

- (void)removeNeedsDiamond:(SSNeedsDiamond *)diamond
{
   [needsDiamondArray removeObjectIdenticalTo:diamond];
}

- (void)removeAllNeedsDiamonds
{
   NSEnumerator *enumerator = [needsDiamondArray objectEnumerator];
   id anObject;

   while (anObject = [enumerator nextObject]) {
      [self removeNeedsDiamond:anObject];
   }
}

- (void)removeAllRelatedNeedsDiamonds
{
   [self removeAllNeedsDiamonds];
   [linkArray makeObjectsPerformSelector:@selector(removeAllNeedsDiamonds)]; //get our links to remove their diamonds
}

-(NSMutableArray *)allRelatedNeedsDiamonds
{
   //get all of our needs diamonds and all of the needs diamonds that belong to our links
   NSMutableArray *array = [NSMutableArray array];
   NSEnumerator *enumerator = [[self linkArray] objectEnumerator];
   id anObject;

   [array addObjectsFromArray:[self needsDiamondArray]];
   
   while (anObject = [enumerator nextObject]) {
      [array addObjectsFromArray:[anObject needsDiamondArray]];
   }

   enumerator = [[self linkFromArray] objectEnumerator];
   while (anObject = [enumerator nextObject]) {
      [array addObjectsFromArray:[[anObject linkObjectConnectingEvent:self] needsDiamondArray]];
   }

   return array;
}

- (BOOL)hasNeedsDiamonds
{
   if([needsDiamondArray count] > 0) {
      return YES;
   }

   return NO;
}

//validation for menu linking
- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
{
   if ([[menuItem title] isEqualToString:NSLocalizedString(@"SetLinkToEvent", nil)]) { //we only want this to work if there is a link from event set
      if(![[[NSDocumentController sharedDocumentController] currentDocument] currentLinkFromEvent] ||
         [[menuItem representedObject] isEqualTo:self]) {
         return NO;
      }
   }

   return YES;
}

- (void)replaceLinkWithName:(NSString *)name withEventLinkObject:(SSEventLinkObject *)linkObject
{
   NSEnumerator *enumerator = [[self linkArray] objectEnumerator];
   id linkDict;
   id proposedLinkObject;

   while (linkDict = [enumerator nextObject]) {
      if([linkDict isKindOfClass:[NSMutableDictionary class]]) { //if it's not it should be a SSEventLinkObject so we don't want to replace it
         proposedLinkObject = [linkDict objectForKey:@"link"];
         if([proposedLinkObject isEqualToString:name]) {
            [linkArray removeObject:linkDict];
            [linkArray addObject:linkObject];
         }
      }
   }
}

- (void)addLinkToObject:(SSEventObject *)linkedToEvent
{
   SSEventLinkObject *linkObject = [[[SSEventLinkObject alloc] initWithLinkedFrom:self
                                                                         linkedTo:linkedToEvent
                                                                             type:[NSNumber numberWithInt:SSConfirmedLinkType]] autorelease];
   [linkArray addObject:linkObject];

   dataChanged = YES;
   [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
}

- (void)addLinkToObjectMenuClick:(NSMenuItem *)item
{
   [self addLinkToObject:[item representedObject]];
   [[[NSDocumentController sharedDocumentController] currentDocument] redisplay]; //need to update the vies so that this link is drawn
}

- (void)addLink:(SSEventLinkObject *)link
{
   [linkArray addObject:link];
   [[link linkedTo] addLinkFrom:self];
}

- (void)addLinkFrom:(SSEventObject *)linkFrom
{
   [[self linkFromArray] addObject:linkFrom];

   dataChanged = YES;
}

- (void)removeLinkMenuClick:(id)sender
{
   id anObject;
   NSEnumerator *enumerator = [linkArray objectEnumerator];

   while ((anObject = [enumerator nextObject])) {
      if(anObject == [sender representedObject]) {
         //we have a link to the selected event
         [anObject removeLinkTo];
         [linkArray removeObject:anObject];
         break;
      }
   }

   //get the document to re display the view
   [[[NSDocumentController sharedDocumentController] currentDocument] reload];

   dataChanged = YES;
   [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
}

- (void)removeLinkFrom:(SSEventObject *)event
{
   [linkFromArray removeObjectIdenticalTo:event];

   dataChanged = YES;
}

- (void)removeLinkTo:(SSEventObject *)link
{
   id anObject;
   NSEnumerator *enumerator = [linkArray objectEnumerator];

   while ((anObject = [enumerator nextObject])) {
      if([anObject linkedTo] == link) {
         //we have a link to the object
         [anObject removeLinkTo]; //have the link object remove the link
         [linkArray removeObjectIdenticalTo:anObject];//remove the link from our linksArray
         break;
      }
   }

   dataChanged = YES;
   [[[NSDocumentController sharedDocumentController] currentDocument] updateChangeCount:NSChangeDone];
}

- (void)removeAllLinks
{
   id anObject;
   NSEnumerator *enumerator = [linkArray objectEnumerator];

   while ((anObject = [enumerator nextObject])) { //removes self from the linkFromArray of objects it is connected to
      [anObject removeLinkTo];
   }
   
   enumerator = [linkFromArray objectEnumerator];

   while ((anObject = [enumerator nextObject])) { //remove links to self in all other events
      [anObject removeLinkTo:self];
   }

   [linkArray removeAllObjects]; //clear our links
   [linkFromArray removeAllObjects];
   dataChanged = YES;
}

- (BOOL)hasLinkToObject:(SSEventObject *)event
{
   id anObject;
   NSEnumerator *enumerator = [linkArray objectEnumerator];

   while ((anObject = [enumerator nextObject])) {
      if([[anObject linkedTo] isEqualTo:event]) {
         return YES;
      }
   }
   return NO;
}

- (BOOL)hasLinkFromSameActor
{
   id anObject;
   NSEnumerator *enumerator = [linkFromArray objectEnumerator];

   while ((anObject = [enumerator nextObject])) {
      if([[anObject actor] isEqualToString:[self actor]]) {
         return YES;
      }
   }
   return NO;
}

//***********************************returns info from eventView
- (NSRect)frame
{
   return [eventView frame];
}

- (float)xPosition
{
   return NSMinX([[self eventView] frame]);
}

- (NSString *)xPositionString
{
   return [[NSNumber numberWithFloat:NSMinX([[self eventView] frame])] stringValue];
}

- (NSPoint)leftTopPoint
{
   return [eventView leftTopPoint];
}

- (NSPoint)leftBottomPoint
{
   return [eventView leftBottomPoint];
}

- (NSPoint)rightTopPoint
{
   return [eventView rightTopPoint];
}

- (NSPoint)rightBottomPoint
{
   return [eventView rightBottomPoint];
}

- (NSPoint)topLeftPoint
{
   return [eventView topLeftPoint];
}

- (NSPoint)topRightPoint
{
   return [eventView topRightPoint];
}

- (NSPoint)bottomLeftPoint
{
   return [eventView bottomLeftPoint];
}

- (NSPoint)bottomRightPoint
{
   return [eventView bottomRightPoint];
}

- (NSPoint)leftPoint
{
   return [eventView leftPoint];
}

- (NSPoint)rightPoint
{
   return [eventView rightPoint];
}

- (NSPoint)topPoint
{
   return [eventView topPoint];
}

- (NSPoint)bottomPoint
{
   return [eventView bottomPoint];
}

- (NSPoint)centerPoint
{
   return [eventView centerPoint];
}

- (NSPoint)positionForNeedsDiamond:(SSNeedsDiamond *)diamond
{
   if([needsDiamondArray containsObject:diamond]) {
      return NSMakePoint(NSMaxX([eventView frame]), (NSMinY([eventView frame]) + ([needsDiamondArray indexOfObject:diamond] * SSNeedsDiamondSize.height)));
   }
   
   return NSZeroPoint;
}

//***********************************
- (void)updateLinks
{
   NSEnumerator *enumerator = [[self linkArray] objectEnumerator];
   id linkObject;

   while (linkObject = [enumerator nextObject]) {
      if([linkObject isKindOfClass:[SSEventLinkObject class]]) {
         [linkObject update];
      }
   }

   [[self linkFromArray] makeObjectsPerformSelector:@selector(updateLinksTo)];
}

- (void)updateLinksTo
{
   NSEnumerator *enumerator = [[self linkArray] objectEnumerator];
   id linkObject;

   while (linkObject = [enumerator nextObject]) {
      if([linkObject isKindOfClass:[SSEventLinkObject class]]) {
         [linkObject update];
      }
   }
}

- (void)strokeLinks
{
   NSEnumerator *enumerator = [[self linkArray] objectEnumerator];
   id linkObject;

   while (linkObject = [enumerator nextObject]) {
      if([linkObject respondsToSelector:@selector(stroke)]) {
         [linkObject stroke];
      }
   }
}

- (void)strokeLinksForVisibleLinkedTo
{
   NSEnumerator *enumerator = [[self linkArray] objectEnumerator];
   id linkObject;

   while (linkObject = [enumerator nextObject]) {
      if([linkObject respondsToSelector:@selector(stroke)]) {
         if([[linkObject linkedTo] isVisible]) {
            [linkObject stroke];
         }
      }
   }
}

- (void)drawDiamonds
{
   [[self needsDiamondArray] makeObjectsPerformSelector:@selector(draw)];
}

- (int)displayMode
{
   return [eventView displayMode];
}

//***********************************sorting code
- (NSComparisonResult)compareByActor:(SSEventObject *)otherEvent
{
   //return our order based on our actor compared to the actor of otherEvent
   return [[self actor] caseInsensitiveCompare:[otherEvent actor]];
}

- (NSComparisonResult)compareByAction:(SSEventObject *)otherEvent
{
   //return our order based on our action compared to the action of otherEvent
   return [[self action] caseInsensitiveCompare:[otherEvent action]];
}

- (NSComparisonResult)compareByObject:(SSEventObject *)otherEvent
{
   //return our order based on our object compared to the object of otherEvent
   return [[self object] caseInsensitiveCompare:[otherEvent object]];
}

- (NSComparisonResult)compareByLocation:(SSEventObject *)otherEvent
{
   //return our order based on our location compared to the location of otherEvent
   return [[self location] caseInsensitiveCompare:[otherEvent location]];
}

- (NSComparisonResult)compareBySource:(SSEventObject *)otherEvent
{
   //return our order based on our source compared to the source of otherEvent
   if([[self source] count] > 0 && [[otherEvent source] count] > 0) {
      //both have at least 1 source so compare as normal
      return [[[self source] objectAtIndex:0] caseInsensitiveCompare:[[otherEvent source] objectAtIndex:0]];
   } else if([[self source] count] > 0) {
      //self has a source but other event doesn't
      return NSOrderedDescending;
   } else {
      return NSOrderedAscending;
   }
   
}

- (NSComparisonResult)compareByBeginTime:(SSEventObject *)otherEvent
{
   //return our order based on our beginTime compared to the beginTime of otherEvent
   return [[self beginTime] compare:[otherEvent beginTime]];
}

- (NSComparisonResult)compareByEndTime:(SSEventObject *)otherEvent
{
   //return our order based on our endTime compared to the endTime of otherEvent
   return [[self endTime] compare:[otherEvent endTime]];
}

- (NSComparisonResult)compareByXPosition:(SSEventObject *)otherEvent
{
   //return our order based on our xPos compared to the xPos of otherEvent
   NSComparisonResult result;
   float pos = [self xPosition] - [otherEvent xPosition];

   if(pos < 0) {
      result = NSOrderedAscending;
   } else {
      if(pos > 0) {
         result = NSOrderedDescending;
      } else {
         result = NSOrderedSame;
      }
   }

   return result;
}

- (NSComparisonResult)compareByRemark:(SSEventObject *)otherEvent
{
   //return our order based on our remark compared to the remark of otherEvent
   return [[self remark] caseInsensitiveCompare:[otherEvent remark]];
}

//***********************************
//- (NSString *)description
//{
  // return [NSString stringWithFormat:@"[unique_id %@]--[actor %@]--[action %@]--[object %@]--[location %@]--[beginTime %@]--[endTime %@]--[source %@]--[remark %@]--[linkArray %@]--[linkFromArray %@]--[address %@]--[eventView %@]--[label %@]--[needsDiamondArray %@] ----------------------------------------------------",
    //  unique_id, actor, action, object, location, beginTime, endTime, source, remark, linkArray, linkFromArray, address, eventView, label, needsDiamondArray];
//}

- (NSMutableDictionary *)pastboardRepresentation
{
   NSMutableDictionary *dict = [NSMutableDictionary dictionary];
   
   [dict setObject:[self class] forKey:@"objectType"];
   [dict setObject:unique_id forKey:@"unique_id"];
   
   if([self actor]) {
      [dict setObject:actor forKey:@"actor"];
   } else {
      [dict setObject:[NSString string] forKey:@"actor"];
   }
   
   if([self action]) {
      [dict setObject:action forKey:@"action"];
   } else {
      [dict setObject:[NSString string] forKey:@"action"];
   }
   
   if([self object]) {
      [dict setObject:object forKey:@"object"];
   } else {
      [dict setObject:[NSString string] forKey:@"object"];
   }
   
   if([self location]) {
      [dict setObject:location forKey:@"location"];
   } else {
      [dict setObject:[NSString string] forKey:@"location"];
   }
   
   if([self source]) {
      [dict setObject:source forKey:@"source"];
   } else {
      [dict setObject:[NSMutableArray array] forKey:@"source"];
   }
   
   if([self remark]) {
      [dict setObject:remark forKey:@"remark"];
   } else {
      [dict setObject:[NSString string] forKey:@"remark"];
   }
   
   if([self label]) {
      [dict setObject:label forKey:@"label"];
   } else {
      [dict setObject:[NSNumber numberWithInt:SSWhiteLabelType] forKey:@"label"];
   }
   
   
   [dict setObject:[beginTime descriptionWithCalendarFormat:@"%Y-%m-%d-%H-%M-%S-%F"] forKey:@"beginTime"];
   [dict setObject:[NSString stringWithFormat:@"%d", [beginTime isEstimated]] forKey:@"beginTimeIsEstimated"];
   [dict setObject:[endTime descriptionWithCalendarFormat:@"%Y-%m-%d-%H-%M-%S-%F"] forKey:@"endTime"];
   [dict setObject:[NSString stringWithFormat:@"%d", [endTime isEstimated]] forKey:@"endTimeIsEstimated"];
   
   
   [dict setObject:address forKey:@"address"];
   

   return dict;
}

//***********************************xml string
- (NSString *)xmlStringRepresentation
{
   NSString *linkString = [NSString string];
   NSEnumerator *enumerator = [[self linkArray] objectEnumerator];
   id anObject;
   NSString *beginType;
   NSString *endType;
   NSString *nsTestString;

   if([beginTime isEstimated]) {
      beginType = @"estimated";
   } else {
      beginType = @"observed";
   }

   if([self endTime]) {
      endType = @"estimated";
   } else {
      endType = @"observed";
   }

   if([self passedNSTest]) {
      nsTestString = @"passed";
   } else {
      nsTestString = @"not passed";
   }
   
   while (anObject = [enumerator nextObject]) {
      //loop through the linkArray and build a string for it
      linkString = [linkString stringByAppendingString:[NSString stringWithFormat:@"\n\t<link type=\"%@\">%@</link>",
         [anObject linkTypeString], [[anObject linkedTo] address]]];
   }
   
   NSString *xmlString = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<mesblock unique_id=\"%@\">\n\t<actor>%@</actor>\n\t<action>%@</action>\n\t<object>%@</object>\n\t<location>%@</location>\n\t<start_time type=\"%@\">\n\t\t<year>%@</year>\n\t\t<month>%@</month>\n\t\t<day>%@</day>\n\t\t<hour>%@</hour>\n\t\t<minute>%@</minute>\n\t\t<second>%@</second>\n\t\t<millisecond>%@</millisecond>\n\t</start_time>\n\t<end_time type=\"%@\">\n\t\t<year>%@</year>\n\t\t<month>%@</month>\n\t\t<day>%@</day>\n\t\t<hour>%@</hour>\n\t\t<minute>%@</minute>\n\t\t<second>%@</second>\n\t\t<millisecond>%@</millisecond>\n\t</end_time>\n\t<source>%@</source>\n\t<remarks>%@</remarks>\n\t<nstest>%@</nstest>\n%@\n</mesblock>",
      [self unique_id], [self actor], [self action], [self object], [self location], beginType,
      [[self beginTime] descriptionWithCalendarFormat:@"%Y"], [[self beginTime] descriptionWithCalendarFormat:@"%m"], [[self beginTime] descriptionWithCalendarFormat:@"%d"],
      [[self beginTime] descriptionWithCalendarFormat:@"%H"], [[self beginTime] descriptionWithCalendarFormat:@"%M"], [[self beginTime] descriptionWithCalendarFormat:@"%S"],
      [[self beginTime] descriptionWithCalendarFormat:@"%F"], endType, [[self endTime] descriptionWithCalendarFormat:@"%Y"], [[self endTime] descriptionWithCalendarFormat:@"%m"],
      [[self endTime] descriptionWithCalendarFormat:@"%d"], [[self endTime] descriptionWithCalendarFormat:@"%H"], [[self endTime] descriptionWithCalendarFormat:@"%M"],
      [[self endTime] descriptionWithCalendarFormat:@"%S"], [[self endTime] descriptionWithCalendarFormat:@"%F"], [self sourceString], [self remark], nsTestString, linkString];
   
   return xmlString;
}

- (NSString *)xmlStringReferenceRepresentation
{
   NSNumber *xPos;

   if([eventView displayMode] == SSMatrixDisplayMode) {
      xPos = [NSNumber numberWithFloat:NSMinX([[self eventView] frame])];
   } else {
      //we need to save the position for matrix view
      xPos = [NSNumber numberWithFloat:[eventView lastXPosition]];
   }
   
   NSString *xmlString = [NSString stringWithFormat:@"\t<block unique_id=\"%@\">\n\t\t<address>%@</address>\n\t\t<label>%@</label>\n\t\t<x unit=\"pixels\">%d</x>\n\t</block>",
      [self unique_id], [self fileName], [self labelString], [xPos intValue]];

   return xmlString;
}

//***********************************coding
- (void)encodeWithCoder:(NSCoder *)coder
{
   [coder encodeObject:unique_id forKey:@"unique_id"];
   [coder encodeObject:actor forKey:@"actor"];
   [coder encodeObject:action forKey:@"action"];
   [coder encodeObject:object forKey:@"object"];
   [coder encodeObject:location forKey:@"location"];
   [coder encodeObject:beginTime forKey:@"beginTime"];
   [coder encodeObject:endTime forKey:@"endTime"];
   [coder encodeObject:source forKey:@"sourceMK2"];
   [coder encodeObject:remark forKey:@"comment"];
   [coder encodeObject:linkArray forKey:@"linkArray"];
   [coder encodeObject:linkFromArray forKey:@"linkFromArray"];
   [coder encodeObject:address forKey:@"address"];
   [coder encodeObject:eventView forKey:@"eventView"];
   [coder encodeObject:label forKey:@"label"];
   [coder encodeObject:needsDiamondArray forKey:@"needsDiamondArray"];
   [coder encodeBool:passedNSTest forKey:@"passedNSTest"];
}

- (id)initWithCoder:(NSCoder *)coder
{
   if(self = [super init]) {
      isLoading = YES;
      [self setUnique_id:[coder decodeObjectForKey:@"unique_id"]];
      [self setActor:[coder decodeObjectForKey:@"actor"]];
      [self setAction:[coder decodeObjectForKey:@"action"]];
      [self setObject:[coder decodeObjectForKey:@"object"]];
      [self setLocation:[coder decodeObjectForKey:@"location"]];

      if([coder containsValueForKey:@"beginTime"]) {
         [self setBeginTime:[coder decodeObjectForKey:@"beginTime"]];
         [self setEndTime:[coder decodeObjectForKey:@"endTime"]];
      } else {
         //this is an older file so we need to convert into the new format
         NSMutableDictionary *bd = [coder decodeObjectForKey:@"beginTimeDictionary"];
         NSMutableDictionary *ed = [coder decodeObjectForKey:@"endTimeDictionary"];
         BOOL beginIsEstimated = ([[bd objectForKey:@"type"] intValue] == SSEstimatedTimeType);
         BOOL endIsEstimated = ([[ed objectForKey:@"type"] intValue] == SSEstimatedTimeType);
         SSChartDate *begin = [SSChartDate chartDateWithCalendarDate:[bd objectForKey:@"time"] isEstimated:beginIsEstimated];
         SSChartDate *end = [SSChartDate chartDateWithCalendarDate:[ed objectForKey:@"time"] isEstimated:endIsEstimated];
         [self setBeginTime:begin];
         [self setEndTime:end];
      }

      if([coder containsValueForKey:@"sourceMK2"]) {
         [self setSource:[coder decodeObjectForKey:@"sourceMK2"]];
      } else {
         NSString *sourceString = [coder decodeObjectForKey:@"source"];
         NSArray *array;

         if(sourceString) {
            array = [NSArray arrayWithObjects:sourceString, nil];
         } else {
            //there was no source so create an empty array
            array = [NSArray array];
         }
         
         [self setSource:array];
      }

      [self setRemark:[coder decodeObjectForKey:@"comment"]];
      [self setLinkArray:[coder decodeObjectForKey:@"linkArray"]];
      [self setLinkFromArray:[coder decodeObjectForKey:@"linkFromArray"]];
      [self setAddress:[coder decodeObjectForKey:@"address"]];
      [self setEventView:[coder decodeObjectForKey:@"eventView"]];
      [eventView setOwner:self];
      [eventView updateBeginTime];
      id lab = [coder decodeObjectForKey:@"label"];
      if([lab isKindOfClass:[NSNumber class]]) {
         [self setLabel:lab];
      } else {
         //early versions held a string so just set it to white 
         [self setLabel:[NSNumber numberWithInt:SSWhiteLabelType]];
      }

      if([coder containsValueForKey:@"needsDiamondArray"]) {
         [self setNeedsDiamondArray:[coder decodeObjectForKey:@"needsDiamondArray"]];
      } else {
         needsDiamondArray = [[NSMutableArray alloc] init];
      }

      if([coder containsValueForKey:@"passedNSTest"]) {
         [self setPassedNSTest:[coder decodeBoolForKey:@"passedNSTest"]];
      }

      isVisible = YES;
      
      isLoading = NO;
   }

   return self;
}

//***********************************

- (void)dealloc
{
   [unique_id release];
   [actor release];
   [action release];
   [object release];
   [location release];
   [beginTime release];
   [endTime release];
   [source release];
   [linkArray release];
   [address release];
   [linkFromArray release];
   [eventView release];
   [needsDiamondArray release];
   [super dealloc];
}

@end



